﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace DotNetUI.Controls
{
    public class DotNetTimer : Control
    {
        static DotNetTimer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DotNetTimer), new FrameworkPropertyMetadata(typeof(DotNetTimer)));
        }
        private Canvas _Canvas;
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _Canvas = (Canvas)this.FindName("_canvas");
            _Canvas = (Canvas)this.Template.FindName("_canvas", this);
            dispatcherTimer.Tick += DispatcherTimer_Tick;
            dispatcherTimer.Interval = TimeSpan.FromSeconds(1);
            dispatcherTimer.Start();
        }
        /// <summary>
        /// 显示文本框
        /// </summary>
        public static readonly DependencyProperty ShowTextProperty = DependencyProperty.Register(nameof(ShowText), typeof(bool), typeof(DotNetTimer));
        /// <summary>
        /// 显示文本框
        /// </summary>
        public bool ShowText { get => this.GetValue<bool>(ShowTextProperty); set => SetValue(ShowTextProperty, value); }
        private void DispatcherTimer_Tick(object sender, EventArgs e)
        {
            if (IsUseTimer)
            {
                this.Hour = DateTime.Now.Hour;
                this.Minute = DateTime.Now.Minute;
                this.Second = DateTime.Now.Second;
            }
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);
            DrawVisual();
        }
        DispatcherTimer dispatcherTimer = new DispatcherTimer();
        /// <summary>
        /// 是否使用自动时间
        /// </summary>
        public static readonly DependencyProperty IsUseTimerProperty = DependencyProperty.Register(nameof(IsUseTimer), typeof(bool), typeof(DotNetTimer));
        /// <summary>
        /// 是否使用自动时间
        /// </summary>
        public bool IsUseTimer { get => this.GetValue<bool>(IsUseTimerProperty); set => SetValue(IsUseTimerProperty, value); }
        /// <summary>
        /// 绘制视图
        /// </summary>
        protected virtual void DrawVisual()
        {
            _Canvas.Children.Clear();
            DrawHour();
            DrawMinute();
            DrawHourText();
            var hour = Hour % 12;
            DrawHourArrow(hour, Minute, Second);
            DrawMinuteArrow(Minute, Second);
            DrawSecondArrow(Second);
            DrawCenterMaker();
            _Canvas.Width = RenderWidth;
        }
        /// <summary>
        /// 时间
        /// </summary>
        public static readonly DependencyProperty HourProperty = DependencyProperty.Register(nameof(Hour), typeof(int), typeof(DotNetTimer), new PropertyMetadata(DateTime.Now.Hour, (s, e) =>
         {
             if (s is DotNetTimer timer)
             {
                 timer.DrawVisual();
             }
         }, (s, e) => 
         {
             if (s is DotNetTimer timer)
             {
                 if (e is int number)
                 {
                     if (number<0)
                     {
                         return 0;
                     }
                 }
             }
             return e;
         }));
        /// <summary>
        /// 当前时间
        /// </summary>
        public int Hour { get => this.GetValue<int>(HourProperty); set => SetValue(HourProperty, value); }
        /// <summary>
        /// 时间
        /// </summary>
        public static readonly DependencyProperty MinuteProperty = DependencyProperty.Register(nameof(Minute), typeof(int), typeof(DotNetTimer), new PropertyMetadata(DateTime.Now.Minute, (s, e) =>
        {
            if (s is DotNetTimer timer)
            {
                if (int.TryParse(e.NewValue?.ToString(), out var n))
                {
                    if (n >= 60)
                    {
                        timer.Hour += 1;
                        timer.Minute = n - 60;
                    }
                    else if (n < 0)
                    {
                        timer.Hour -= 1;
                        timer.Minute = n + 60;
                    }
                }
                timer.DrawVisual();

            }
        }));
        /// <summary>
        /// 当前时间
        /// </summary>
        public int Minute { get => this.GetValue<int>(MinuteProperty); set => SetValue(MinuteProperty, value); }
        /// <summary>
        /// 时间
        /// </summary>
        public static readonly DependencyProperty SecondProperty = DependencyProperty.Register(nameof(Second), typeof(int), typeof(DotNetTimer), new PropertyMetadata(DateTime.Now.Second, (s, e) =>
        {
            if (s is DotNetTimer timer)
            {
                if (int.TryParse(e.NewValue?.ToString(), out var n))
                {
                    if (n >= 60)
                    {
                        timer.Minute += 1;
                        timer.Second = n - 60;
                    }
                    else if (n < 0)
                    {
                        timer.Minute -= 1;
                        timer.Second = n + 60;
                    }
                }
                timer.DrawVisual();
            }
        }));
        /// <summary>
        /// 当前时间
        /// </summary>
        public int Second { get => this.GetValue<int>(SecondProperty); set => SetValue(SecondProperty, value); }
        #region Minute
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public static readonly DependencyProperty MinuteLengthProperty = DependencyProperty.Register(nameof(MinuteLength), typeof(double), typeof(DotNetTimer));
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public double MinuteLength { get => this.GetValue<double>(MinuteLengthProperty); set => SetValue(MinuteLengthProperty, value); }
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public static readonly DependencyProperty MinuteThicknessProperty = DependencyProperty.Register(nameof(MinuteThickness), typeof(double), typeof(DotNetTimer));
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public double MinuteThickness { get => this.GetValue<double>(MinuteThicknessProperty); set => SetValue(MinuteThicknessProperty, value); }
        /// <summary>
        /// 小时颜色
        /// </summary>
        public static readonly DependencyProperty MinuteBrushProperty = DependencyProperty.Register(nameof(MinuteBrush), typeof(Brush), typeof(DotNetTimer));
        /// <summary>
        /// 小时颜色
        /// </summary>
        public Brush MinuteBrush { get => this.GetValue<Brush>(MinuteBrushProperty); set => SetValue(MinuteBrushProperty, value); }
        #endregion

        #region Hour
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public static readonly DependencyProperty HourLengthProperty = DependencyProperty.Register(nameof(HourLength), typeof(double), typeof(DotNetTimer));
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public double HourLength { get => this.GetValue<double>(HourLengthProperty); set => SetValue(HourLengthProperty, value); }
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public static readonly DependencyProperty HourThicknessProperty = DependencyProperty.Register(nameof(HourThickness), typeof(double), typeof(DotNetTimer));
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public double HourThickness { get => this.GetValue<double>(HourThicknessProperty); set => SetValue(HourThicknessProperty, value); }
        /// <summary>
        /// 小时颜色
        /// </summary>
        public static readonly DependencyProperty HourBrushProperty = DependencyProperty.Register(nameof(HourBrush), typeof(Brush), typeof(DotNetTimer));
        /// <summary>
        /// 小时颜色
        /// </summary>
        public Brush HourBrush { get => this.GetValue<Brush>(HourBrushProperty); set => SetValue(HourBrushProperty, value); }
        #endregion

        #region HourText
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public static readonly DependencyProperty HourTextSizeProperty = DependencyProperty.Register(nameof(HourTextSize), typeof(double), typeof(DotNetTimer));
        /// <summary>
        /// 小时指针长度
        /// </summary>
        public double HourTextSize { get => this.GetValue<double>(HourTextSizeProperty); set => SetValue(HourTextSizeProperty, value); }
        /// <summary>
        /// 小时颜色
        /// </summary>
        public static readonly DependencyProperty HourTextBrushProperty = DependencyProperty.Register(nameof(HourTextBrush), typeof(Brush), typeof(DotNetTimer));
        /// <summary>
        /// 小时颜色
        /// </summary>
        public Brush HourTextBrush { get => this.GetValue<Brush>(HourTextBrushProperty); set => SetValue(HourTextBrushProperty, value); }
        /// <summary>
        /// 文字偏移量
        /// </summary>

        public static readonly DependencyProperty HourTextOffsetProperty = DependencyProperty.Register(nameof(HourTextOffset), typeof(double), typeof(DotNetTimer));
        /// <summary>
        /// 文字偏移量
        /// </summary>
        public double HourTextOffset { get => this.GetValue<double>(HourTextOffsetProperty); set => SetValue(HourTextOffsetProperty, value); }
        #endregion
        /// <summary>
        /// 中心点掩膜
        /// </summary>
        public static readonly DependencyProperty CenterMarkProperty = DependencyProperty.Register(nameof(CenterMark), typeof(FrameworkElement), typeof(DotNetTimer));
        /// <summary>
        /// 中间的掩膜
        /// </summary>
        public FrameworkElement CenterMark { get => this.GetValue<FrameworkElement>(CenterMarkProperty); set => SetValue(CenterMarkProperty, value); }

        /// <summary>
        /// 秒样式
        /// </summary>
        public static readonly DependencyProperty SecondArrowProperty = DependencyProperty.Register(nameof(SecondArrow), typeof(Line), typeof(DotNetTimer));
        /// <summary>
        /// 秒样式
        /// </summary>
        public Line SecondArrow { get => this.GetValue<Line>(SecondArrowProperty); set => SetValue(SecondArrowProperty, value); }
        /// <summary>
        /// 秒样式
        /// </summary>
        public static readonly DependencyProperty MinuteArrowProperty = DependencyProperty.Register(nameof(MinuteArrow), typeof(Line), typeof(DotNetTimer));
        /// <summary>
        /// 秒样式
        /// </summary>
        public Line MinuteArrow { get => this.GetValue<Line>(MinuteArrowProperty); set => SetValue(MinuteArrowProperty, value); }
        /// <summary>
        /// 秒样式
        /// </summary>
        public static readonly DependencyProperty HourArrowProperty = DependencyProperty.Register(nameof(HourArrow), typeof(Line), typeof(DotNetTimer));
        /// <summary>
        /// 秒样式
        /// </summary>
        public Line HourArrow { get => this.GetValue<Line>(HourArrowProperty); set => SetValue(HourArrowProperty, value); }
        /// <summary>
        /// 小时指针属性
        /// </summary>

        public static readonly DependencyProperty HourArrowLengthProperty = DependencyProperty.Register(nameof(HourArrowLength), typeof(double), typeof(DotNetTimer));
        /// <summary>
        /// 小时指针属性
        /// </summary>
        public double HourArrowLength { get => this.GetValue<double>(HourArrowLengthProperty); set => SetValue(HourArrowLengthProperty, value); }


        private double AngleToRad(double angle)
        {
            var c = Math.PI / 180;
            return c * angle;
        }
        private Point CenterPoint
        {
            get
            {
                var centerx = (RenderWidth / 2);
                var centery = (RenderWidth / 2);
                return new Point(centerx, centery);
            }
        }
        /// <summary>
        /// 获取坐标点
        /// </summary>
        /// <param name="length">总长度</param>
        /// <param name="count">个数</param>
        /// <param name="reduce">缩减值</param>
        /// <param name="offset">偏移</param>
        /// <returns></returns>
        private Point[] GetPoints(double length, int count, Point offset, int reduce = 90)
        {
            List<Point> points = new List<Point>();
            var slice = 360 / count;
            for (int i = 0; i < count; i++)
            {
                var angle = (slice * i) - reduce;
                var r = (length / 2);
                var y = (Math.Sin(AngleToRad(angle)) * r) + offset.X;
                var x = (Math.Cos(AngleToRad(angle)) * r) + offset.Y;

                points.Add(new Point(x, y));
            }
            return points.ToArray();
        }
        private Point GetPoint(double length, double count, Point offset, int reduce, int i)
        {
            var slice = 360 / count;
            var angle = (slice * i) - reduce;
            var r = (length / 2);
            var y = (Math.Sin(AngleToRad(angle)) * r) + offset.X;
            var x = (Math.Cos(AngleToRad(angle)) * r) + offset.Y;
            return new Point(x, y);
        }
        private double RenderWidth
        {
            get
            {
                return Math.Min(this._Canvas.ActualWidth, this._Canvas.ActualHeight);
            }
        }
        private void DrawHour()
        {
            var points1 = GetPoints(RenderWidth, 12, CenterPoint);
            var points2 = GetPoints(RenderWidth - HourLength, 12, CenterPoint);
            for (int i = 0; i < points1.Length; i++)
            {
                var p1 = points1[i];
                var p2 = points2[i];
                Line line = new Line();
                line.X1 = p1.X;
                line.Y1 = p1.Y;
                line.X2 = p2.X;
                line.Y2 = p2.Y;
                line.StrokeStartLineCap = PenLineCap.Flat;
                line.StrokeEndLineCap = PenLineCap.Round;
                line.Stroke = HourBrush;
                line.StrokeThickness = HourThickness;
                _Canvas.Children.Add(line);
            }


        }
        private void DrawHourText()
        {
            var points3 = GetPoints(RenderWidth - HourLength - HourTextSize - HourTextOffset, 12, CenterPoint, 60);
            for (int i = 0; i < 12; i++)
            {
                var p = points3[i];
                Label label = new Label();
                label.Content = i + 1;
                label.Foreground = HourTextBrush;
                label.FontSize = HourTextSize;
                Canvas.SetLeft(label, p.X - HourTextSize);
                Canvas.SetTop(label, p.Y - HourTextSize);
                _Canvas.Children.Add(label);
            }
        }
        private void DrawMinute()
        {
            var points1 = GetPoints(RenderWidth, 60, CenterPoint);
            var points2 = GetPoints(RenderWidth - MinuteLength, 60, CenterPoint);
            for (int i = 0; i < points1.Length; i++)
            {
                if (i % 5 == 0)
                {
                    continue;
                }
                var p1 = points1[i];
                var p2 = points2[i];
                Line line = new Line();
                line.X1 = p1.X;
                line.Y1 = p1.Y;
                line.X2 = p2.X;
                line.Y2 = p2.Y;
                line.StrokeStartLineCap = PenLineCap.Round;
                line.StrokeEndLineCap = PenLineCap.Round;
                line.Stroke = MinuteBrush;
                line.StrokeThickness = MinuteThickness;
                _Canvas.Children.Add(line);
            }
        }
        /// <summary>
        /// 绘制秒指针
        /// </summary>
        /// <param name="second"></param>
        private void DrawSecondArrow(int second)
        {

            if (second >= 60)
            {
                return;
            }

            if (second < 0)
            {
                return;
            }

            var points3 = GetPoints(RenderWidth * 4 / 5, 60, CenterPoint, 90);
            var p = points3[second];
            SecondArrow.X1 = CenterPoint.X;
            SecondArrow.Y1 = CenterPoint.Y;
            SecondArrow.X2 = p.X;
            SecondArrow.Y2 = p.Y;
            _Canvas.Children.Add(SecondArrow);
        }
        /// <summary>
        /// 绘制分钟指针
        /// </summary>
        /// <param name="minute"></param>
        /// <param name="second">秒</param>
        private void DrawMinuteArrow(int minute, int second)
        {

            if (second >= 60)
            {
                return;
            }
            if (minute >= 60)
            {
                return;
            }

            if (second < 0)
            {
                return;
            }
            if (minute < 0)
            {
                return;
            }

            var p = GetPoint(RenderWidth * 3 / 5, 60 * 60, CenterPoint, 90, minute * 60 + second);
            MinuteArrow.X1 = CenterPoint.X;
            MinuteArrow.Y1 = CenterPoint.Y;
            MinuteArrow.X2 = p.X;
            MinuteArrow.Y2 = p.Y;
            _Canvas.Children.Add(MinuteArrow);
        }
        /// <summary>
        /// 绘制小时指针
        /// </summary>
        /// <param name="hour"></param>
        /// <param name="minute">分钟</param>
        /// <param name="second">小时</param>
        private void DrawHourArrow(int hour, int minute, int second)
        {
            if (second >= 60)
            {
                return;
            }
            if (minute >= 60)
            {
                return;
            }
            if (hour >= 24)
            {
                return;
            }
            if (second < 0)
            {
                return;
            }
            if (minute < 0)
            {
                return;
            }
            if (hour < 0)
            {
                return;
            }
            var p = GetPoint(RenderWidth * 2 / 5, 12 * 60 * 60, CenterPoint, 90, hour * 60 * 60 + minute * 60 + second);
            HourArrow.X1 = CenterPoint.X;
            HourArrow.Y1 = CenterPoint.Y;
            HourArrow.X2 = p.X;
            HourArrow.Y2 = p.Y;
            _Canvas.Children.Add(HourArrow);
        }
        private void DrawCenterMaker()
        {
            Canvas.SetLeft(CenterMark, CenterPoint.X - CenterMark.Width / 2);
            Canvas.SetTop(CenterMark, CenterPoint.Y - CenterMark.Height / 2);
            _Canvas.Children.Add(CenterMark);
        }
    }
}
